//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_XML_CNODE_H_
#define _BUOLA_XML_CNODE_H_

#include <buola/xml/cdoc.h>
#include <buola/algorithm/comparison.h>
#include <buola/iterator/facade.h>
#include <buola/iterator/range.h>

namespace buola { namespace xml {

class INode;
    
///\addtogroup xml
///@{
    
//! Class representing a node of XML document. 
//! Each node may have associated name and value strings, which are available through name() and value() functions. 
//! Interpretation of name and value depends on type of the node.
//! Type of node can be determined by using type() function.
//! <br><br>
//! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. 
//! Thus, this text must persist in the memory for the lifetime of node.
//! \param Ch Character type to use.
class CNode
{
    friend CDoc;
    friend INode;
    
private:
    CNode(CDoc::SNode *pNode)
        :   mNode(pNode)
    {}
    
public:
    CNode()
        :   mNode(nullptr)
    {}
        
    CNode(const CNode &pRH)=default;
    CNode &operator=(const CNode &pRH)=default;
    
    ENodeType Type() const              {   return mNode->mType;    }
    const string_view &Name() const     {   return mNode->mName;    }
    const string_view &Value() const    {   return mNode->mValue;   }
    std::string Lang() const;
    std::string Content() const;
    
    bool MatchesName(const std::string &pName) const;

    CDoc &Document() const              {   return *mNode->mDoc;    }
    bool Null() const                   {   return !mNode;          }
    explicit operator bool() const      {   return (bool)mNode;     }
    
    CNode FirstChild(const string_view &pName,bool pIgnoreCase=false) const;
    
    CNode FirstChild() const
    {
        return &*mNode->mChildren.begin();
    }

    size_t ChildCount() {   return mNode->mChildren.size(); }
    
    CNode NthChild(size_t n)
    {
        return &*std::next(mNode->mChildren.begin(),n);
    }
    
    CRange<INode> Children();
    ///\todo access to attributes

    void SetType(ENodeType pType) const;

    ///\return a CNode pointing to the created child
    CNode NewChild(const std::string &pName,const std::string &pContent=std::string()) const;
    
    void Prepend(const CNode &pChild) const;
    void Append(const CNode &pChild) const;
    void Insert(const CNode &pWhere,const CNode &pChild) const;

    void RemoveFirstChild() const;
    void RemoveLastChild() const;
    void RemoveChild(const CNode &pChild) const;
    void ClearChildren() const;
    void ClearAttributes() const;
    ///gets the XML attrib pName. Returns true if it was found
    bool GetAttrib(const std::string &pName,std::string &pReturn) const;
    ///returns the XML attrib pName as a std::string
    std::string GetStringAttrib(const std::string &pName,const std::string& =std::string());
    ///returns the XML attrib pName as a std::wstring
    std::wstring GetWStringAttrib(const std::string &pName,const std::wstring& =std::wstring());
    ///returns the XML attrib pName as a boolean
    bool GetBoolAttrib(const std::string&,bool=false);
    ///returns the XML attrib pName as an integer
    int GetIntAttrib(const std::string&,int=0);
    ///returns the XML attrib pName as a double
    double GetDoubleAttrib(const std::string&,double=0);
    void GetListAttrib(const std::string&,std::vector<std::wstring>&);
    void GetListAttrib(const std::string&,std::vector<std::string>&);
    
    void SetAttrib(const std::string &pName,const std::string &pValue) const;
    void SetStringAttrib(const std::string&,const std::wstring&);
    void SetIntAttrib(const std::string&,int);
    void SetDoubleAttrib(const std::string&,double);
    void SetBoolAttrib(const std::string&,bool=true);
    void SetListAttrib(const std::string&,const std::vector<std::wstring>&);
    void SetListAttrib(const std::string&,const std::vector<std::string>&);
    
    std::string ToString() const;
    void AppendTo(std::string &pString) const;

private:
    CNode Next() const
    {
        auto i=intrusive::list<CDoc::SNode>::s_iterator_to(*mNode);
        
        return (++i).operator->();
    }
    
    CNode Prev() const
    {
        auto i=intrusive::list<CDoc::SNode>::s_iterator_to(*mNode);
        
        return (--i).operator->();
    }
    
private:
    CDoc::SNode *mNode;
};

class INode : public IFacade<INode,std::bidirectional_iterator_tag,const CNode>
{
    typedef IFacade<INode,std::bidirectional_iterator_tag,const CNode> TBase;
    friend TBase;

public:
    ///initializes the iterator from a CNode
    explicit INode(const CNode &pNode)
        :   mNode(pNode)
    {}
    
private:
    ///returns the current value of the counter.
    const CNode &Dereference() const
    {
        return mNode;
    }
    
    ///increments the value of the iterator
    void Increment()
    {
        mNode=mNode.Next();
    }

    ///decrements the value of the iterator
    void Decrement()
    {
        mNode=mNode.Prev();
    }

    ///returns true if the two iterators contain the same value
    bool Equal(const INode &pO) const
    {
        return mNode.mNode==pO.mNode.mNode;
    }

    CNode mNode;
};

inline CRange<INode> CNode::Children()
{
    return {INode(&*mNode->mChildren.begin()),INode(&*mNode->mChildren.end())};
}

///@}

/*namespace xml*/ } /*namespace buola*/ }

#endif
